package com.example.hikiot.utils;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;

/**
 * 接口数据加解密工具类
 **/
public class RSAUtils {

    private static final String RSA_ALGORITHM = "RSA";
    private static final String CHARSET = "UTF-8";
    private static final int MAX_ENCRYPT_BLOCK = 117;
    private static final int MAX_DECRYPT_BLOCK = 128;

    private RSAUtils() {
    }

    /**
     * 应用秘钥加密接口
     *
     * @param data 需要加密的字符串
     * @param secret 应用的秘钥（Secret）
     * @return 加密后的字符串
     * @throws Exception
     */
    public static String encryptByPrivateKey(String data, String secret) throws Exception {
        RSAPrivateKey privateKey = getPrivateKey(secret);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] dataBytes = URLEncoder.encode(data, CHARSET).getBytes(CHARSET);
        byte[] enBytes = null;
        for (int i = 0; i < dataBytes.length; i += MAX_ENCRYPT_BLOCK) {
            byte[] subBytes = cipher.doFinal(ArrayUtils.subarray(dataBytes, i, i + MAX_ENCRYPT_BLOCK));
            enBytes = ArrayUtils.addAll(enBytes, subBytes);
        }
        return Base64.encodeBase64String(enBytes);
    }

    /**
     * 应用秘钥解密接口
     *
     * @param data 需要解密的字符串
     * @param secret 应用的秘钥（Secret）
     * @return 解密后的字符串
     * @throws Exception
     */
    public static String decryptByPrivateKey(String data, String secret) throws Exception {
        RSAPrivateKey privateKey = getPrivateKey(secret);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] dataBytes = Base64.decodeBase64(data.getBytes(CHARSET));
        int inputLen = dataBytes.length;
        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            int offset = 0;
            byte[] cache;
            int i = 0;
            while (inputLen - offset > 0) {
                if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
                }
                out.write(cache, 0, cache.length);
                i += 1;
                offset = i * MAX_DECRYPT_BLOCK;
            }
            String outStr = out.toString(CHARSET);
            return URLDecoder.decode(outStr, CHARSET);
        }
    }

    private static RSAPrivateKey getPrivateKey(String privateKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
        return (RSAPrivateKey) factory.generatePrivate(keySpec);
    }

}
